package com.charm.xft.bill.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.charm.xft.bill.entity.*;
import com.charm.xft.bill.service.*;
import com.cmb.xft.open.api.BaseReqInf;
import com.cmb.xft.open.api.Constants;
import com.cmb.xft.open.api.HttpResponseData;
import com.cmb.xft.open.api.XftOpenApiReqClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.ofdrw.converter.export.ImageExporter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ISyncBillServiceImpl implements ISyncBillService {
    @Autowired
    IXftConfigService xftConfigService;
    @Autowired
    IXftBillService xftBillService;
    @Autowired
    IXftBillInvoiceCloudObjectService invoiceCloudObjectService;

    @Autowired
    IXftBillFileService billFileService;
    @Value("${spring.uploadPath}")
    String uploadPath;

    @Value("${spring.debug}")
    Boolean DEBUG = true;

    @Value("${spring.visitPath}")
    String visitPath;


    @Value("${spring.verifyUser}")
    String verifyUser;
    String companyId = "XFZ35649";
    /* 开放平台 appId */
    String appId = "a1813feb-7b22-4e38-8c71-20ec473bd2bb";


    /* 开放平台 authoritySecret */
    String authoritySecret = "5ab0644cb05884db9623f73ee500888471438cc4e99f8e604b9e0001324af68d";
    /* 请求路径（以测试环境为例）*/
    String BILL_LIST_API = "https://api.cmbchina.com/itrip/xft-api/v1/bills/queryDetails";
    String BILL_DOWNLOAD_COMMAND_API = "https://api.cmbchina.com/itrip/xft-api/v1/invoice/batchInvoiceExport";
    String BILL_DOWNLOAD_QUERY_STATUS_API = "https://api.cmbchina.com/itrip/xft-api/v1/invoice/queryProcess";

    String BILL_DOWNLOAD_FILE_API = "https://api.cmbchina.com/itrip/xft-api/v1/invoice/downloadHisInvoice";
    String BILL_VERIFY_API = "https://api.cmbchina.com/itrip/xft-api/v1/approve/pass";


    @PostConstruct
    void init() {
        ThreadUtil.execAsync(() -> {
            while (true) {

                try {
                    Thread.sleep(5000);
                    syncTask();
                    sendDownloadCommandTask();
                    queryDownloadStatusTask();
                    downloadFile();
                    unZipDownloadFile();
                    checkBill();
                    verifyBill();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });

    }

    private void checkBill() {
        try {
            LambdaQueryWrapper<XftBill> condition = new LambdaQueryWrapper<>();
            condition.ge(XftBill::getApplyTime, DateUtil.offsetDay(new Date(), -1));
            condition.in(XftBill::getAttachFileDownloadStatus, "4","3","5");
            condition.ge(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

            List<XftBill> list = xftBillService.list(condition);
            for (XftBill xftBill : list) {
                List<XftBillInvoiceCloudObject> list1 = invoiceCloudObjectService.list(new LambdaQueryWrapper<XftBillInvoiceCloudObject>()
                        .eq(XftBillInvoiceCloudObject::getBillId, xftBill.getId())
                );
                for (XftBillInvoiceCloudObject xftBillInvoiceCloudObject : list1) {

                    if (StrUtil.isNotBlank(xftBillInvoiceCloudObject.getInvoiceNumber())) {
                        long count = billFileService.count(
                                new LambdaQueryWrapper<XftBillFile>()
                                        .eq(XftBillFile::getXftBillId, xftBill.getId())
                                        .eq(XftBillFile::getInvoiceNumber, xftBillInvoiceCloudObject.getInvoiceNumber())
                        );


                        if (count == 0) {

                            log.warn("找不到订单【{}】-发票{}的附件，需重新下载", xftBill.getId(), xftBillInvoiceCloudObject.getInvoiceNumber());
                            XftBill xftBill1 = new XftBill();
                            xftBill1.setAttachFileDownloadStatus("6");
                            xftBillService.update(xftBill1,new LambdaQueryWrapper<XftBill>().eq(XftBill::getId, xftBill.getId()));
                        }
                    }
                }
            }
        } catch (Exception e) {

        }

    }

    private void verifyBill() {
        if (!DEBUG) {
            LambdaUpdateWrapper<XftBill> xftBillLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            LambdaUpdateWrapper<XftBill> eq = xftBillLambdaUpdateWrapper
                    .in(XftBill::getAttachFileDownloadStatus, "4","10")
            .ge(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

            List<XftBill> list = xftBillService.list(eq);

            for (XftBill xftBill : list) {
                try {
                    BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
                    /* 3.请求报文构建：字符串形式(推荐使用方式) */
                    /* 4.调用接口 */
                    JSONObject entries = new JSONObject();
                    JSONArray objects = new JSONArray();
                    entries.putOnce("businessId", xftBill.getId());
                    objects.add(entries);
                    HashMap<String, Object> stringStringHashMap = new HashMap<>();
                    stringStringHashMap.put("OPAUID", companyId);


                    String[] split = verifyUser.split(",");
                    for (String s : split) {
                        HttpResponseData httpResponseData = XftOpenApiReqClient.doCommonPostReq(baseReqInf,
                                BILL_VERIFY_API, stringStringHashMap,
                                new JSONObject().putOnce("businessList", objects)
                                        .putOnce("empId", s).toString());
                        Boolean success = JSONUtil.parseObj(httpResponseData.getBody()).getBool("success");
                        if (success) {
                            break;
                        }
                    }

                    XftBill updteEntity = new XftBill();
                    updteEntity.setId(xftBill.getId());
                    updteEntity.setAttachFileDownloadStatus("5");
                    xftBillService.updateById(updteEntity);
                    log.info("订单【{}】审批完成", xftBill.getId());
                } catch (Exception e) {
                    log.info("订单【{}】审批失败 原因:{}", xftBill.getId(), e.getMessage());
                }

            }
        }


    }

    private void unZipDownloadFile() {
        //解壓對應的文件，并將文件對應關係放入到子表中
        LambdaUpdateWrapper<XftBill> xftBillLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<XftBill> eq = xftBillLambdaUpdateWrapper
                .eq(XftBill::getAttachFileDownloadStatus, "3")
                .gt(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

        List<XftBill> list = xftBillService.list(eq);

        list.stream().forEach(xftBill -> {
            try {
                XftBill updteEntity = new XftBill();
                updteEntity.setId(xftBill.getId());
                updteEntity.setAttachFileDownloadStatus("4");
                File unzip = ZipUtil.unzip(String.format("%s/%s", uploadPath, xftBill.getAttachFileDownloadName()), String.format("%s/%s/", uploadPath, xftBill.getId()));
                List<String> files = FileUtil.listFileNames(String.format("%s/%s", uploadPath, xftBill.getId()));
                //判断文件就给如果就PDF，尝试转成图片

                //将长文件名去除无用字段
                for(String file : files){
                    if(file.contains("AWSAccessKeyId")){
                        log.warn(String.format("处理无效文件名:%s", file));
                        File file1 = new File(String.format("%s/%s/%s", uploadPath, xftBill.getId(),file));
                        String newName = file1.getName().replaceAll("&AWSAccessKeyId.*", "");
                        FileUtil.rename(file1, newName, true);
                        log.warn(String.format("处理无效文件名:%s 完成，新文件名为%s", file, newName));
                    }
                }

                files = FileUtil.listFileNames(String.format("%s/%s", uploadPath, xftBill.getId()));
                for (String file : files) {
                    if (FileNameUtil.isType(file, "PDF", "pdf")) {
                        try {
                            pdf2png(String.format("%s/%s", uploadPath, xftBill.getId()), file, "png");
                        } catch (Exception e) {

                        }
                    }
                    if (FileNameUtil.isType(file, "ofd", "OFD")) {
                        try {
                            getOfdPng(String.format("%s/%s", uploadPath, xftBill.getId()), file);
                        } catch (Exception e) {

                        }

                    }
                }

                files = FileUtil.listFileNames(String.format("%s/%s", uploadPath, xftBill.getId()));

                billFileService.remove(new LambdaQueryWrapper<XftBillFile>().eq(XftBillFile::getXftBillId, xftBill.getId()));

                for (String file : files) {
                    XftBillFile xftBillFile = new XftBillFile();
                    xftBillFile.setFileSuffix(FileNameUtil.getSuffix(file));
                    try {
                        String result = ReUtil.extractMulti(".*_([\\w,\\-]+).*\\.[^\\.]+$", file, "$1");
                        String result1 = ReUtil.extractMulti(".*_(\\d+)_(\\d+).*\\.[^\\.]+$" , file, "$1");

                        List<XftBillInvoiceCloudObject> list1 = invoiceCloudObjectService.list(
                                new LambdaQueryWrapper<XftBillInvoiceCloudObject>()
                                        .eq(XftBillInvoiceCloudObject::getBillId, xftBill.getId())
                                .eq(XftBillInvoiceCloudObject::getTrafficEcode,  result)
                        );
                        if (!list1.isEmpty()) {
                            xftBillFile.setInvoiceNumber(list1.get(0).getInvoiceNumber());
                        }else{
                            xftBillFile.setInvoiceNumber(result);
                        }

                        xftBillFile.setInvoiceCode(result1);
                    } catch (Exception e) {

                    }

                    xftBillFile.setFileName(file);
                    xftBillFile.setXftBillId(xftBill.getId());
                    xftBillFile.setFilePath(String.format("%s/%s/%s", uploadPath, xftBill.getId(), file));
                    xftBillFile.setCreateTime(LocalDateTime.now());
                    xftBillFile.setVisitLink(String.format("%s/%s/%s", visitPath, xftBill.getId(), URLEncodeUtil.encode(file)));
                    xftBillFile.setId(IdUtil.getSnowflakeNextIdStr());
                    billFileService.save(xftBillFile);
                }
                xftBillService.updateById(updteEntity);
                log.info("订单【{}】解压缩完成", xftBill.getId());
            } catch (Exception e) {
                log.info("订单【{}】解压缩失败 原因:{}", xftBill.getId(), e.getMessage());

            } finally {

            }

        });
    }


    /**
     * 转换全部的pdf
     *
     * @param fileAddress 文件地址
     * @param filename    PDF文件名
     * @param type        图片类型
     */
    public static void pdf2png(String fileAddress, String filename, String type) {
        // 将pdf装图片 并且自定义图片得格式大小
        File file = new File(fileAddress + "/" + filename);
        PDDocument doc = null;
        try {
            doc = PDDocument.load(file);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for (int i = 0; i < pageCount; i++) {
                BufferedImage image = renderer.renderImage(i, 2); // Windows native DPI
                // BufferedImage srcImage = resize(image, 240, 240);//产生缩略图
                ImageIO.write(image, type, new File(fileAddress + "/" + filename + "-" + (i + 1) + "." + type));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                doc.close();
            } catch (Exception e) {

            }
        }
    }

    public static void getOfdPng(String ofdPathName, String file) {
        Path ofdPath = Paths.get(String.format("%s/%s", ofdPathName, file));
        Path imgDirPath = Paths.get(String.format("%s/%s.tmp", ofdPathName, file));
        try (ImageExporter exporter = new ImageExporter(ofdPath, imgDirPath, "PNG", 20d)) {
            exporter.export();
            List<File> files = FileUtil.loopFiles(String.format("%s/%s.tmp", ofdPathName, file));
            for (File file1 : files) {
                FileUtil.copyFile(file1.getAbsolutePath(),
                        String.format("%s/%s-%s", ofdPathName, file, file1.getName()));
            }
        } catch (IOException e) {
        }
    }

    private void downloadFile() {
        LambdaUpdateWrapper<XftBill> xftBillLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<XftBill> eq = xftBillLambdaUpdateWrapper
                .eq(XftBill::getAttachFileDownloadStatus, "2")
        .ge(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

        List<XftBill> list = xftBillService.list(eq);

        LambdaUpdateWrapper<XftBill> eq1 = xftBillLambdaUpdateWrapper
                .eq(XftBill::getAttachFileDownloadStatus, "1")
                .lt(XftBill::getCreateTime, DateUtil.offsetMinute(new Date(),1));
        List<XftBill> list1 = xftBillService.list(eq1);
        list.addAll(list1);


        list.stream().forEach(xftBill -> {
            try {
                /* 1.公共信息构建 */
                if (StrUtil.isBlank(xftBill.getAttachFileDownloadName())) {
                    xftBill.setAttachFileDownloadName("%s.zip".format(xftBill.getId()));
                }
                BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
                /* 2.请求报文构建：字符串形式(推荐使用方式) */
                /* 3.调用接口 */
                HashMap<String, Object> stringStringHashMap = new HashMap<>();
                stringStringHashMap.put("OPAUID", companyId);
                stringStringHashMap.put("billId", xftBill.getId());
                stringStringHashMap.put("fileName", xftBill.getAttachFileDownloadName());
                stringStringHashMap.put("option", 1);

                HttpResponseData httpResponseData = XftOpenApiReqClient.doGetFileDownloadReq(baseReqInf, BILL_DOWNLOAD_FILE_API, stringStringHashMap);
                File file = FileUtil.writeBytes(httpResponseData.getBody().getBytes(Constants.CHARSET_ISO), String.format("%s\\%s", uploadPath, xftBill.getAttachFileDownloadName()));

                XftBill updteEntity = new XftBill();
                updteEntity.setId(xftBill.getId());
                updteEntity.setAttachFileDownloadStatus("3");
                xftBillService.updateById(updteEntity);
                log.info("订单【{}】下载完成", xftBill.getId());
            } catch (Exception e) {
                log.info("订单【{}】下载失败 原因:{}", xftBill.getId(), e.getMessage());
            }

        });

    }

    private void queryDownloadStatusTask() {

        LambdaUpdateWrapper<XftBill> xftBillLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<XftBill> eq = xftBillLambdaUpdateWrapper
                .eq(XftBill::getAttachFileDownloadStatus, "1")
                .gt(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

        List<XftBill> list = xftBillService.list(eq);

        list.parallelStream().forEach(xftBill -> {
            try {
                BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
                /* 2.请求报文构建：字符串形式(推荐使用方式) */
                /* 3.调用接口 */
                HashMap<String, Object> stringStringHashMap = new HashMap<>();
                stringStringHashMap.put("OPAUID", companyId);
                stringStringHashMap.put("requestId", xftBill.getAttachFileDownloadReqId());
                HttpResponseData httpResponseData = XftOpenApiReqClient.doCommonGetReq(baseReqInf, BILL_DOWNLOAD_QUERY_STATUS_API, stringStringHashMap);
                JSONArray objects = JSONUtil.parseArray(httpResponseData.getBody());
                XftBill updteEntity = new XftBill();
                updteEntity.setId(xftBill.getId());
                if (objects.size() > 0) {
                    String status = objects.getByPath("[0].status", String.class);
                    if (status.equals("2")) {
                        JSONArray fileList = objects.getByPath("[0].fileList", JSONArray.class);
                        String collect = fileList.stream().map(i -> i.toString()).collect(Collectors.joining());
                        updteEntity.setAttachFileDownloadName(collect);
                        updteEntity.setAttachFileDownloadStatus("2");
                        xftBillService.updateById(updteEntity);
                    }else{


                    }
                } else {
                    //沒有可下載的文件
                    updteEntity.setAttachFileDownloadStatus("10");
                    xftBillService.updateById(updteEntity);

                }
                log.info("订单【{}】查询下载状态完成", xftBill.getId());
            } catch (Exception e) {

                log.info("订单【{}】查询下载状态失败 原因:{}", xftBill.getId(), e.getMessage());

            }
        });
    }

    private void sendDownloadCommandTask() {

        LambdaUpdateWrapper<XftBill> xftBillLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<XftBill> eq = xftBillLambdaUpdateWrapper
                .in(XftBill::getAttachFileDownloadStatus, "0","-1","6")
                .gt(XftBill::getCreateTime, DateUtil.offsetDay(new Date(),-7));

        List<XftBill> list = xftBillService.list(eq);
        list.parallelStream().forEach(xftBill -> {
            try {
                /* 1.公共信息构建 */
                BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
                /* 2.请求报文构建：字符串形式(推荐使用方式) */
                /* 3.调用接口 */
                HashMap<String, Object> stringStringHashMap = new HashMap<>();
                stringStringHashMap.put("OPAUID", companyId);
                stringStringHashMap.put("option", "1");
                if (xftBill.getAttachFileDownloadStatus().equals("6") ||xftBill.getAttachFileDownloadStatus().equals("-1")) {
                    stringStringHashMap.put("restart", true);
                    log.info("订单【{}】发送下载命令 (重新进行RESTART)", xftBill.getId());

                }else{
                    stringStringHashMap.put("restart", false);
                    log.info("订单【{}】发送下载命令 (使用之前的下载结果)", xftBill.getId());

                }
                JSONArray objects = new JSONArray();
                objects.add(xftBill.getId());

                HttpResponseData httpResponseData = null;
                httpResponseData = XftOpenApiReqClient.doCommonPostReq(baseReqInf, BILL_DOWNLOAD_COMMAND_API, stringStringHashMap, objects.toString());
                JSON parse = JSONUtil.parse(httpResponseData.getBody());
                if (parse.getByPath("success", Boolean.class)) {
                    int code = parse.getByPath("data", Integer.class);
                    XftBill updteEntity = new XftBill();
                    updteEntity.setId(xftBill.getId());
                    updteEntity.setAttachFileDownloadReqId(String.valueOf(code));
                    updteEntity.setAttachFileDownloadStatus("1");
                    xftBillService.updateById(updteEntity);
                }
                log.info("订单【{}】发送下载命令 完成", xftBill.getId());
            } catch (Exception e) {
                log.info("订单【{}】发送下载命令失败 原因:{}", xftBill.getId(), e.getMessage());

            }
        });

    }

    private void syncTask() {
        XftConfig xftConfig = xftConfigService.getById(1);

        if (xftConfig == null) {
            log.info("获取数据库配置失败");
        } else {
            log.info(String.format("最后一次同步时间为：%s", xftConfig.getLastTime().toString()));
        }
        //读取上次读取的偏移地址
        //读取一批数据
        Date from = Date.from(xftConfig.getLastTime().plusSeconds(1).toInstant(ZoneOffset.ofHours(8)));
        List<XftBill> bills = syncFxtBillListByStartTimeAndEndTme(from, new Date());

        Optional<XftBill> max = bills.stream().max((i, j) -> {
            return i.getApplyTime().isAfter(j.getApplyTime()) ? 1 : -1;
        });
        if (max.isPresent()) {
            xftConfig.setLastTime(max.get().getApplyTime());
            xftConfigService.updateById(xftConfig);
        }
    }


    public String getUserMememberByBillId(String billId) {
        try {
            String url = String.format("https://api.cmbchina.com/itrip/xft-api/v1/bills/detail/%s", billId);
            /* 2.公共信息构建 */
            BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
            /* 3.请求报文构建：字符串形式(推荐使用方式) */
            /* 4.调用接口 */
            HashMap<String, Object> stringStringHashMap = new HashMap<>();
            stringStringHashMap.put("OPAUID", companyId);
            HttpResponseData httpResponseData = XftOpenApiReqClient.doCommonGetReq(baseReqInf, url, stringStringHashMap);
            JSON parse = JSONUtil.parse(httpResponseData.getBody());
            String memmberId = parse.getByPath("billInfo.aplEmpNumber", String.class);

            return memmberId;
        } catch (Exception e) {
            return "";
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public List<XftBill> syncFxtBillListByStartTimeAndEndTme(Date startTime, Date EndTime) {

        BaseReqInf baseReqInf = new BaseReqInf(companyId, appId, authoritySecret);
        HashMap<String, Object> params = new HashMap<>();
        params.put("OPAUID", companyId);

        JSONObject requestBody = new JSONObject();
        requestBody.putOnce("applyTimStart", DateUtil.formatDateTime(startTime));
        requestBody.putOnce("applyTimEnd", DateUtil.formatDateTime(EndTime));

        HttpResponseData httpResponseData = null;
        try {
            httpResponseData = XftOpenApiReqClient.doCommonPostReq(baseReqInf,
                    BILL_LIST_API, params, requestBody.toString());
            JSONObject root = JSONUtil.parseObj(httpResponseData.getBody());
            JSONArray billExtDetailInfoDtos = root.getJSONObject("data").getJSONArray("billExtDetailInfoDtos");

            List<XftBill> collect = billExtDetailInfoDtos.parallelStream().map(i -> {
                JSONObject jb = (JSONObject) i;
                String billId = jb.getJSONObject("billInfo").getStr("billId");
                String memember = getUserMememberByBillId(billId);
                XftBill xftBill = xftBillService.saveBillAllEntityByJSONObject(jb, memember);
                return xftBill;
            }).collect(Collectors.toList());
            return collect;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }


}
